//
// Created by wxd on 2025/7/6.
//

#pragma once

#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <typeinfo>
#include <stdexcept>
#include <any>


#define ANY_ERROR(config, key, type) \
std::ostringstream oss; oss << "[" << typeid(this).name() << "]" << "[" << key << "] " << "not " << type << "\n"; throw std::runtime_error(oss.str());

enum class ValueType {
    BOOL, SHORT, INT, UINT, STRING, FLOAT, DOUBLE, C_STRING
};

class IConfig {
public:
    virtual ~IConfig() = default;
    virtual void set(const std::string& key, ValueType type, std::any value) = 0;
    [[nodiscard]] virtual std::pair<ValueType, std::any> get(const std::string& key) const = 0;
    [[nodiscard]] virtual std::string toString() const = 0;

    [[nodiscard]] virtual bool getBool(const std::string& key) const = 0;
    [[nodiscard]] virtual short getShort(const std::string& key) const = 0;
    [[nodiscard]] virtual int getInt(const std::string& key) const = 0;
    [[nodiscard]] virtual unsigned int getUint(const std::string& key) const = 0;
    [[nodiscard]] virtual float getFloat(const std::string& key) const = 0;
    [[nodiscard]] virtual double getDouble(const std::string& key) const = 0;
    [[nodiscard]] virtual std::string getString(const std::string& key) const = 0;
};


class Config : public IConfig {
public:
    // 通过Builder创建
    class ConfigBuilder {
    public:
        ConfigBuilder() : config(std::make_unique<Config>()) {}
        ConfigBuilder(const ConfigBuilder&) = delete;
        ConfigBuilder& operator=(const ConfigBuilder&) = delete;

        ConfigBuilder& with(const std::string& key, const ValueType type, const std::any& value) {
            config->set(key, type, value);
            return *this;
        }

        Config* build() {
            return config.release();
        }

    private:
        std::unique_ptr<Config> config;
    };

    Config() = default;
    Config(const Config&) = delete;
    Config& operator=(const Config&) = delete;

    std::string toString() const override {
        std::ostringstream oss;
        for (auto& entry : configMap) {
            oss << entry.first << ", ";
        }
        return oss.str();
    }

    bool getBool(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::BOOL) {
            ANY_ERROR(typeid(this).name(), key, "bool");
        } else {
            return std::any_cast<bool>(value.second);
        }
    }

    short getShort(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::SHORT) {
            ANY_ERROR(typeid(this).name(), key, "short");
        } else {
            return std::any_cast<short>(value.second);
        }
    }

    int getInt(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::INT) {
            ANY_ERROR(typeid(this).name(), key, "int");
        } else {
            return std::any_cast<int>(value.second);
        }
    }

    unsigned int getUint(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::UINT) {
            ANY_ERROR(typeid(this).name(), key, "unsigned int");
        } else {
            return std::any_cast<unsigned int>(value.second);
        }
    }

    float getFloat(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::FLOAT) {
            ANY_ERROR(typeid(this).name(), key, "float");
        } else {
            return std::any_cast<float>(value.second);
        }
    }

    double getDouble(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::DOUBLE) {
            ANY_ERROR(typeid(this).name(), key, "double");
        } else {
            return std::any_cast<double>(value.second);
        }
    }

    std::string getString(const std::string &key) const override {
        auto value = get(key);

        if (value.first != ValueType::STRING && value.first != ValueType::C_STRING) {
            ANY_ERROR(typeid(this).name(), key, "string");
        } else {
            if (value.first == ValueType::C_STRING) {
                return std::any_cast<const char*>(value.second);
            } else {
                return std::any_cast<std::string>(value.second);
            }
        }
    }
protected:
    std::unordered_map<std::string, std::pair<ValueType, std::any>> configMap;

private:
    void set(const std::string& key, const ValueType type, const std::any value) override {
        configMap[key] = { type, value };
    }

    std::pair<ValueType, std::any> get(const std::string &key) const override {
        const auto entry = configMap.find(key);

        if (entry == configMap.end()) {
            std::ostringstream oss;
            oss << "[" << typeid(this).name() << "]" << " " << "[" << key << "] " << "not found" << "\n";
            throw std::runtime_error(oss.str());
        }

        return entry->second;
    }
};